#include "mixp_skt_server.h"
#include "mixp_skt_client.h"

static void on_connect(int, void *);
static void on_server_error(int, void *);

mixp_skt_server_t *mixp_skt_server_new(int port, const char *unixpath)
{
    int fd = -1;

    if (unixpath != NULL && strlen(unixpath) > 0)
    {
        fd = skt_unix_bind_listen(unixpath, port);
    }
    else
    {
        fd = skt_tcp_bind_listen(NULL, port);
    }

    if (fd == -1)
    {
        loge("start skt server failed. unixpath: %s, port:%d!", unixpath, port);
        return NULL;
    }

    mixp_skt_server_t *server = MALLOC(mixp_skt_server_t);
    server->evbase = gevent_base_create();
    if (server->evbase == NULL)
    {
        loge("gevent_base_create failed!");
        FREE(server);
        return NULL;
    }

    server->port = port;
    server->unixpath = unixpath;
    server->fd = fd;
    gevent_t *ev = gevent_create(fd, on_connect, NULL, on_server_error, server);
    if (-1 == gevent_add(server->evbase, ev))
    {
        loge("connect event add failed!");
        gevent_destroy(ev);
        gevent_base_destroy(server->evbase);
        FREE(server);

        return NULL;
    }

    server->ev = ev;

    server->dict_fd2client = hash_create(10240);
    server->dict_uuid2fd = hash_create(10240);

    return server;
}

static void on_connect(int fd, void *arg)
{
    mixp_skt_server_t *server = (mixp_skt_server_t *)arg;
    uint32_t ip;
    uint16_t port;

    int client_fd = skt_accept(fd, &ip, &port);
    if (client_fd == -1)
    {
        printf("on_connect failed: %d\n", errno);
        return;
    }

    mixp_skt_client_t *client = mixp_skt_client_new(server, client_fd, ip, port);
    if (client != NULL)
    {
        char fd_str[9];
        char sessionid_str[9];
        char *fdval = (char *)calloc(1, 9);
        snprintf(fd_str, sizeof(fd_str), "%08x", fd);
        snprintf(sessionid_str, sizeof(sessionid_str), "%08x", client->sessionid);
        snprintf(fdval, 9, "%08x", fd);
        hash_set(server->dict_fd2client, fd_str, (char *)client);
        hash_set(server->dict_uuid2fd, sessionid_str, fdval);

        logi("connection made. client:%s", client->info);
    }
    else
    {
        logw("create client failed.");
    }
}

static void on_server_error(int fd, void *arg)
{
    loge("[ on_server_error ]: fd: %d, errno: %d", fd, errno);
}

void mixp_skt_server_free(mixp_skt_server_t *server)
{
    if (server->ev)
    {
        gevent_del(server->evbase, server->ev);
        gevent_destroy(server->ev);
    }

    hash_destroy(server->dict_fd2client);
    hash_destroy(server->dict_uuid2fd);

    gevent_base_loop_stop(server->evbase);
    pthread_join(server->tid, NULL);
    gevent_base_destroy(server->evbase);

    FREE(server);
}

static void *event_thread(void *arg)
{
    mixp_skt_server_t *server = (mixp_skt_server_t *)arg;
    gevent_base_loop(server->evbase);
    return NULL;
}

int mixp_skt_server_run(mixp_skt_server_t *server)
{
    int ret = -1;
    pthread_create(&server->tid, NULL, event_thread, server);

    return ret;
}